import * as path from "path";
import * as fs from "fs";
import * as os from "os";
import * as childProcess from "child_process";
import * as hx from "hbuilderx";
import * as dayjs from "dayjs"
import * as i18n from "./i18n";

const DEFAULT_GROUP_NAME = "default";
const CREATE_GROUP_NAME = "create_group";

export const COMMAND_IDS:any = {
   "hx_project_group_cmd_add_project_to_group": "hx_project_group_cmd_add_project_to_group",
   "hx_project_group_cmd_switch_to_group": "hx_project_group_cmd_switch_to_group",
   "hx_project_group_cmd_group_controller": "hx_project_group_cmd_group_controller",
   "hx_project_group_cmd_open_group": "hx_project_group_cmd_open_group",
   "hx_project_group_cmd_open_all_for_group": "hx_project_group_cmd_open_all_for_group",
   "hx_project_group_cmd_close_group":"hx_project_group_cmd_close_group"
}

export async function onCommand(id: string, param: any) {
    const configer = new Configer;
    if (! await configer.init()) {
        return;
    }

    switch (id) {
        case COMMAND_IDS.hx_project_group_cmd_add_project_to_group:
            return onAddProjectToGroup(configer, param);
        case COMMAND_IDS.hx_project_group_cmd_switch_to_group:
            return onSwitchToGroup(configer, param);
        case COMMAND_IDS.hx_project_group_cmd_group_controller:
            return onGroupControllerOpen(configer, param);
        case COMMAND_IDS.hx_project_group_cmd_open_group:
            return onOpenGroup(configer, param);
        case COMMAND_IDS.hx_project_group_cmd_open_all_for_group:
            return onOpenForGroup(configer, param);
        case COMMAND_IDS.hx_project_group_cmd_close_group:
            return onCloseGroup(configer, param);
        default:
            break;
    }
}

async function onAddProjectToGroup(configer: Configer, param: any) {
   // 选择的项目可能是单个也可能是多个
   let projectSelections: string[] = (param instanceof Array ? param : [param]).map((p: any) => p.fsPath);
   if (!projectSelections.length) {
      return;
   }
   let hasTitle = false;
   projectSelections = projectSelections.filter(pro=>{
      if(configer.isCroupContainerProject(pro)){
         hasTitle = true;
         return false
      }
      
      return true;
   })
   
   hasTitle ? hx.window.showWarningMessage(i18n.SKIP_GROUP_TITLE_PROJECT, [i18n.OK]) : void 0;
   
   if (!projectSelections.length) {
      return;
   }
   

   let currentGroupName = configer.getCurrentGroup();
   let allGroups = configer.getAllGroup();

   try {
      const result = await hx.window.showQuickPick(createPickItems(), { placeHolder: i18n.PICK_DIALOG_PLACEHOLDER_TEXT(currentGroupName) });
      if (result && result.value && configer.lock()) {
         if (result.value === CREATE_GROUP_NAME) {
            await createGroup()
         }
         else {
            await selectGroup(result.value);
         }
      }

   } catch (e) {
      console.log("stop");
   }

   configer.free()

   function createPickItems() {
      return [{
         label: i18n.CREATE_GROUP_NAME,
         description: '',
         value: CREATE_GROUP_NAME
      },
      ...createGroupNamePickItems(configer, allGroups)
      ]
   }

   async function createGroup() {
      let name = await hx.window.showInputBox({
         prompt: i18n.GOURP_NAME_DIALOG_PLACEHOLDER_TEXT
      });

      if (name === "") {
         hx.window.showErrorMessage(i18n.ERROR_TIP_GROUP_NAME_CANNOT_BE_EMPTY, [i18n.YES]);
         await createGroup()
      }
      else if (configer.hasGroup(name)) {
         hx.window.showErrorMessage(i18n.ERROR_TIP_GROUP_NAME_EXISTS, [i18n.YES]);
         await createGroup();
      }
      else {
         configer.setGroupProjects(name, [])
         configer.flushConfig();

         onAddProjectToGroup(configer, param);
      }
   }

   async function selectGroup(name: string) {
      const targetGroupProjects = configer.getGroupProjects(name);
      const targetGooupProjectsAfter = targetGroupProjects.concat(projectSelections.filter(ps => !targetGroupProjects.includes(ps)));
      configer.setGroupProjects(name, targetGooupProjectsAfter);

      configer.flushConfig();
      
      hx.window.setStatusBarMessage(i18n.INFO_TIP_ADD_PROJECT_FINISHED, 5000, 'info');
   }
}

async function onSwitchToGroup(configer: Configer, _: any) {
   const currentGroupName = configer.getCurrentGroup();
   let allGroups = configer.getAllGroup().filter(name => name !== currentGroupName);

   try {
      const result = await hx.window.showQuickPick(createGroupNamePickItems(configer, allGroups), { placeHolder: i18n.PICK_DIALOG_SWIFT_GROUP_PLACEHOLDER_TEXT(currentGroupName) });
      if (result && result.value && configer.lock()) {
         hx.window.setStatusBarMessage(i18n.INFO_TIP_SWITCHING_GROUP, 0, 'info');

         const targetGroupName = result.value;
         const targetGroupProjects = configer.getGroupProjects(targetGroupName, true);

         const currentGroupProjects = configer.getGroupProjects(currentGroupName, true);

         const cli = new CliCommandExecer(configer);
         currentGroupProjects.length ? await cli.closeProjects(currentGroupProjects) : void 0;
         targetGroupProjects.length ? await cli.closeProjects(targetGroupProjects) : void 0;
         targetGroupProjects.length ? await cli.openProjects(targetGroupProjects) : void 0;
         
         configer.setCurrentGroup(targetGroupName);
         configer.getSystemConfig("hx_project_group.delete.empty.group.after.switch") ? configer.cleanEmpty() : void 0;
         configer.flushConfig();
         
         hx.window.setStatusBarMessage(i18n.INFO_TIP_SWITCHED_GROUP, 5000, 'info');
      }
   } catch (e) { }

   configer.free()
}

async function onGroupControllerOpen(configer: Configer, _: any) {
   let dialog = new ControllerDialog(configer);
   return dialog.exec();
}

async function onOpenGroup(configer: Configer, _: any) {
   const currentGroupName = configer.getCurrentGroup();
   let allGroups = configer.getAllGroup().filter(name => name !== currentGroupName);

   try {
      const result = await hx.window.showQuickPick(createGroupNamePickItems(configer, allGroups), { placeHolder: i18n.PICK_DIALOG_OPEN_GROUP_PLACEHOLDER_TEXT(currentGroupName) });
      if (result && result.value && configer.lock()) {
         // hx.window.setStatusBarMessage(i18n.INFO_TIP_SWITCHING_GROUP, 0, 'info');

         const opendProjects = await configer.getAllOpendProjects();

         const targetGroupName = result.value;
         const targetGroupProjects = configer.getGroupProjects(targetGroupName, true).filter(project => !opendProjects.includes(project));

         const cli = new CliCommandExecer(configer);
         targetGroupProjects.length ? await cli.closeProjects(targetGroupProjects) : void 0;
         targetGroupProjects.length ? await cli.openProjects(targetGroupProjects) : void 0;
         
         // hx.window.setStatusBarMessage(i18n.INFO_TIP_SWITCHED_GROUP, 5000, 'info');
      }
   } catch (e) { }

   configer.free()
}

async function onOpenForGroup(configer: Configer, _: any) {
    hx.window.showFormDialog({
        formItems: [
            {
                "type": "fileSelectInput",
                "name": "dir",
                "mode": "folder",
                "label": "",
                "placeholder": "请选择分组位置",
                "value": "",
            }
        ],
        title: "打开为分组",
        subtitle: "将指定的目录打开为分组，目录下所有目录都打开为项目",
        width: 480,
        height: 180,
        submitButtonText: "提交(&S)",
        cancelButtonText: "取消(&C)",
        onOpened: function () { },
    }).then(async (res: any) => {
        let dir = res.dir;
        if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
            let dirName = path.basename(dir);
            if (configer.hasGroup(dirName)) {
                return;
            }
            console.log("dirName",dirName);
            let projects: string[] = fs.readdirSync(dir).map(item => path.join(dir, item)).filter(file => fs.existsSync(file) && fs.statSync(file).isDirectory());
            // console.log("所有项目", projects);
            if (projects.length && configer.lock()) {
                configer.setGroupProjects(dirName, projects);
                configer.flushConfig();

                const cli = new CliCommandExecer(configer);
                await cli.closeProjects(projects);
                await cli.openProjects(projects);
                configer.free();
            }
        }
    });
}

async function onCloseGroup(configer:Configer, param:any){
    console.log(configer, param)
    const groupPath = param.fsPath;
    if(groupPath && typeof groupPath === "string"){
        const res = /-----=\[\s*(.*)\s*]=-----/.exec(groupPath);
        if(res && res.length){
            const groupName = res[1].trim();
            // console.log(groupName);
            if(DEFAULT_GROUP_NAME === groupName){
                
                return;
            }
            
            if(configer.hasGroup(groupName) && configer.lock()){
                const isCurrentGroup = configer.getCurrentGroup() === groupName;
                const pendingProjects = configer.getGroupProjects(groupName, true);
                
                const cli = new CliCommandExecer(configer);
                await cli.closeProjects(pendingProjects);
                
                if(isCurrentGroup){
                    const defaultProjects = configer.getGroupProjects(DEFAULT_GROUP_NAME, true);
                    await cli.openProjects(defaultProjects);
                    configer.setCurrentGroup(DEFAULT_GROUP_NAME);
                    configer.flushConfig();
                }
            }
            configer.free();
        }
    }
}

/**
 * @internal
  */
function createGroupNamePickItems(configer: Configer, groupNames: string[]) {
   return groupNames.map(groupName => {
      return {
         label: groupName,
         description: i18n.INFO_TIP_PROJECTS_COUNT(configer.getGroupProjects(groupName).length),
         value: groupName
      }
   })
}

/**
 * @internal
 */ 
function toNormalPath(filePath: string): string {
   return filePath.replace(/\\/g, "/");
}

class Configer {
   public readonly APP_DATA: string = hx.env.appData;
   public readonly APP_ROOT: string = hx.env.appRoot;
   public readonly IS_EXTENSITION: boolean = /\.extensions_development/.test(this.APP_DATA);

   protected readonly LOCK_NAME: string = "index.lock";

   private _configDir: string;
   private _lockFile: string;
   private _configFile: string;
   private _config: {
      current_group: string,
      groups: {
         [key: string]: string[]
      },
      backup: {
         [key: string]: {
            time: string,
            alias: string,
            projects: string[]
         }[]
      }
   }
   
   private _groupContainerProjectsDir: string;
   constructor() {
      this._configDir = path.join(this.APP_DATA, "extensions", "hx_project_group");

      this._lockFile = path.join(this._configDir, this.LOCK_NAME);

      this._configFile = path.join(this._configDir, "settings.json");

      this._config = {
         current_group: DEFAULT_GROUP_NAME,
         groups: {
            [DEFAULT_GROUP_NAME]: []
         },
         backup: {}
      }
      
      this._groupContainerProjectsDir = path.join(this._configDir, "group_container_projects");
   }

   async init(): Promise<boolean> {
      if (!fs.existsSync(this._configDir)) {
         let suc = fs.mkdirSync(this._configDir, { recursive: true });
         if (!suc) {
            return false;
         }
      }

      if (fs.existsSync(this._configFile)) {
         try {
            this._config = JSON.parse(fs.readFileSync(this._configFile).toString());
         } catch (e) {
            return false;
         }
      }
      else {
         this._config.groups[DEFAULT_GROUP_NAME] = await this.getAllOpendProjects()
      }
      
      if(!fs.existsSync(this._groupContainerProjectsDir)){
         let suc = fs.mkdirSync(this._groupContainerProjectsDir, { recursive: true });
         if (!suc) {
            return false;
         }
      }

      return true
   }
   
   lock(): boolean {
      if (fs.existsSync(this._lockFile)) {
         hx.window.showWarningMessage(i18n.ERROR_TIP_TASK_BUSSY, [i18n.YES]);
         return false;
      }
      fs.writeFileSync(this._lockFile, "running");
      return true;
   }
   free() {
      if (fs.existsSync(this._lockFile)) {
         fs.rmSync(this._lockFile);
      }
   }

   setGroupProjects(name: string, projects: string[]) {
      if (name === "") {
         return;
      }

      this._config.groups[name] = projects.map(p => toNormalPath(p));
   }
   getGroupProjects(name: string, render: boolean = false): string[] {
      if(!this.hasGroup(name)){
         return [];
      }
      
      if(!render){
         return this._config.groups[name] ?? [];
      }
      else{
         
         let projects = [...this._config.groups[name]]
         const title = this.createGroupContainerProject(name);
         title ? projects = [title, ...projects] : void 0;

         return projects;
      }
   }
   
   setCurrentGroup(name: string) {
      if (!name) {
         return;
      }

      this._config.current_group = name;
   }
   getCurrentGroup(): string {
      return this._config.current_group;
   }
   
   hasGroup(name: string): boolean {
      return Object.keys(this._config.groups).includes(name);
   }
   getAllGroup(): string[] {
      return Object.keys(this._config.groups).filter(name => name !== DEFAULT_GROUP_NAME);
   }
   
   cleanEmpty(){
      const curGroup = this.getCurrentGroup();
      const allGroup = this.getAllGroup().filter(name => name !== curGroup && name !== DEFAULT_GROUP_NAME);

      for(let groupName of allGroup){
         let projects = this.getGroupProjects(groupName);
         if(!projects.length){
            delete this._config.groups[groupName];
            delete this._config.backup[groupName];
            this.destroyGroupContainerProject(groupName);
         }
      }
   }

   hasBackup(groupName: string): boolean {
      let backup = this._config.backup[groupName];
      return !!backup && !!backup.length;
   }
   getBackupInfos(groupName: string): [string /* time */, string /* alias */][] {
      if(!this.hasBackup(groupName)){
         return [];
      }
      
      return this._config.backup[groupName].map(info=>{
         return [info.time, info.alias]
      })
   }
   getBackupProjects(groupName: string, time: string): string[] {
      if(!this.hasBackup(groupName)){
         return [];
      }
      
      let info = this._config.backup[groupName].find(info => info.time === time);
      return info?.projects ?? []
   }
   renameBackup(groupName: string, time: string, alias: string) {
      if (!this.hasBackup(groupName)) {
         return;
      }

      let info = this._config.backup[groupName].find(info => info.time === time);
      info ? info.alias = alias : void 0;
   }
   removeBackup(groupName: string, time: string) {
      if(!this.hasBackup(groupName)){
         return;
      }
      
      let infos = this._config.backup[groupName].filter(info => info.time !== time);
      infos.length ? this._config.backup[groupName] = infos : delete this._config.backup[groupName];
   }
   cleanBackup(groupName: string) {
      if(!this.hasBackup(groupName)){
         return;
      }
      delete this._config.backup[groupName];
   }
   backup(groupName: string, projects: string[], alias?: string) {
      let time = dayjs(Date.now()).format("YYYY-MM-DD HH:mm:ss SSS");
      let infos = this._config.backup[groupName] ?? [];
      infos.push({
         time:`${time}`,
         alias: alias ?? `${time}`,
         projects
      })
      
      this._config.backup[groupName] = infos;
   }
   
   flushConfig() {
      try {
         fs.writeFileSync(this._configFile, JSON.stringify(this._config, null, "  "));
      } catch (e) { }
   }
   
   createGroupContainerProject(groupName: string): string | undefined {
      let begin = toNormalPath(path.join(this._groupContainerProjectsDir, `-----=[ ${groupName} ]=-----`));
      
      if(!fs.existsSync(begin)){
         let succ = fs.mkdirSync(begin, { recursive: true });
         if (!succ) {
            return undefined;
         }
      }

      return begin;
   }
   destroyGroupContainerProject(groupName: string) {
      let begin = toNormalPath(path.join(this._groupContainerProjectsDir, `-----=[ ${groupName} ]=-----`))

      if (fs.existsSync(begin)) {
         fs.rmSync(begin, { recursive: true });
      }
   }
   isCroupContainerProject(project: string): boolean {
      return toNormalPath(project).toLowerCase().startsWith(toNormalPath(this._groupContainerProjectsDir).toLowerCase());
   }

   getSystemConfig(key: string): any | undefined {
      let config = hx.workspace.getConfiguration()
      return config.get(key);
   }
   
   async getAllOpendProjects(): Promise<string[]> {
      let folders = [];
      try{
         folders = await hx.workspace.getWorkspaceFolders() ?? [];
      }catch(e){}
      
      return folders.map((folder: any) => toNormalPath(folder.uri.fsPath));
   }
}

class CliCommandExecer {
   public readonly CLI_EXEC_PROGRAM: string;
   protected readonly TASK_WAIT_TIME = 10;
   constructor(private _configer: Configer, private _args?: string[]) {
      this.CLI_EXEC_PROGRAM = path.resolve(_configer.APP_ROOT, os.platform() === "win32" ? "cli.exe" : "../MacOS/cli");
   }

   run(arg?: string[]): Promise<boolean> {
      return new Promise((resolve, reject) => {
         let realArg = arg ?? this._args ?? [];
         console.log("cli:", this.CLI_EXEC_PROGRAM);
         console.log("开始执行cli命令", "参数：", realArg);
         childProcess.execFile(this.CLI_EXEC_PROGRAM, realArg, { timeout: 5000 }, (err, stdout, stderr) => {
            if (err) {
               console.error("执行失败：",err);
               reject(void 0);
               return
            }

            console.error("out:", stdout);
            stderr ? console.error("err:", stderr) :void 0;
            resolve(stderr ? false : true)
         })
      })
   }

   closeProjects(projects: string[]): Promise<boolean[]> {
      return new Promise<boolean[]>(async (resolve, _) => {
         // 获取额外的执行参数
         const extArgs = await this.extArgs();
         // 获取所有打开的项目
         const openedProjects: string[] = await this._configer.getAllOpendProjects();
         // 过筛后反转
         const stack: string[] = [...projects.filter(p => openedProjects.includes(p))].reverse();
         const result: boolean[] = [];
         while (stack.length) {
            let cur = stack.shift()!;
            try {
               result.push(await this.run(["project close", "--path", cur, ...extArgs]));
               await this.waitFor(this.TASK_WAIT_TIME);
            } catch (e) {
               result.push(false);
            }
         }
      
         resolve(result);
      })
   }

   openProjects(projects: string[]): Promise<boolean[]> {
      return new Promise<boolean[]>(async (resolve, _) => {
         const extArgs = await this.extArgs();
         const stack: string[] = [...projects];
         const result: boolean[] = [];
         while (stack.length) {
            let cur = stack.shift()!;
            try {
               result.push(await this.run(["project open", "--path", cur, ...extArgs]));
               await this.waitFor(this.TASK_WAIT_TIME);
            } catch (e) {
               result.push(false);
            }
         }

         resolve(result);
      })
   }
   
   async extArgs(): Promise<string[]> {
      if(this._configer.IS_EXTENSITION){
         return ["--host", "HBuilderX-extension"];
      }
      
      let isSingleWindow = true;
      try {
         isSingleWindow = await this.run(["--help"]);
      } catch (e) {
         console.error(e);
      }
      return isSingleWindow ? [] : ["--host", "HBuilderX"];
   }

   waitFor(time: number): Promise<void> {
      return new Promise((res,_)=>{
         let id  = setTimeout(()=>{
            clearTimeout(id);
            res();
         }, time);
      })
   }
}

class ControllerDialog {
   private _currentGroupName: string;
   private _currentGroupProjects: string[];
   private _backupList: [string, string][];
   private _diglogWidgetData: {
      dialogTitle: string,
      dialogSubTitle: string,
      dialogWidth: number,
      dialogHeight: number
      selectedProjectIndex: number,
      sortProjectTopOrBottomCheckState: boolean,
      selectedBackupIndex: number
   };
   private readonly _dialogWidgetName = {
      projectList: "projectList",
      projectControlButtonGroup: "projectControlButtonGroup",
      projectControlButton: {
         separator: "separator",
         topOrBottom: "button_topOrBottom",
         up: "button_up",
         down: "button_down",
         delete: "button_delete",
         deleteAll: "button_deleteAll",
         backup: "button_backup"
      },
      backuplist: "backuplist",
      backupControlButtonGroup: "backupControlButtonGroup",
      backupControlButton: {
         separator: "separator",
         rename: "button_rename",
         delete: "button_delete",
         deleteAll: "button_deleteAll",
         revocer: "button_revocer"
      }
   };
   
   constructor(private _configer: Configer) {
      this._currentGroupName = _configer.getCurrentGroup();
      this._currentGroupProjects = _configer.getGroupProjects(this._currentGroupName);
      this._backupList = _configer.getBackupInfos(this._currentGroupName)

      this._diglogWidgetData = {
         dialogTitle: i18n.CONTROL_DIALOG_TITLE,
         dialogSubTitle: i18n.CURRRNT_GROUP_NAME(this._currentGroupName),
         dialogWidth: 1000,
         dialogHeight: 600,
         selectedProjectIndex: 0,
         sortProjectTopOrBottomCheckState: false,
         selectedBackupIndex: 0
      }
   }

   async exec(): Promise<void> {
      const _this = this;
      let result = await hx.window.showFormDialog({
         ...this.createForm(),
         onChanged: async function (field: any, value: any, allData: any) {
            if (await _this.onChanged(field, value, allData)) {
               this.updateForm(_this.createForm());
            }
         }
      });
      
      if (result && result.buttonIndex === 1) {
         if (this._configer.lock()) {
            this._configer.setGroupProjects(this._currentGroupName, this._currentGroupProjects);
            this._configer.flushConfig();

            let projects = this._configer.getGroupProjects(this._currentGroupName, true);
            
            let cli = new CliCommandExecer(this._configer);
            await cli.closeProjects(projects);
            await cli.openProjects(projects);

            this._configer.free();
         }
      }
      
      return;
   }

   private createForm() {
      let {
         dialogTitle,
         dialogSubTitle,
         dialogWidth,
         dialogHeight,
         selectedProjectIndex,
         sortProjectTopOrBottomCheckState,
         selectedBackupIndex
      } = this._diglogWidgetData;

      const formItems: any[] = [
         {
            type: "list",
            title: i18n.PROJECTS_IN_GROUP,
            name: this._dialogWidgetName.projectList,
            columnStretches: [1],
            items: this.createListWidgetItems(this._currentGroupProjects.map(project => [project])),
            value: selectedProjectIndex,
            multiSelection: false,
         },
         {
            type: "widgetGroup",
            name: this._dialogWidgetName.projectControlButtonGroup,
            widgets: [{
               type: "checkBox",
               name: "topOrBottom",
               text: i18n.TOP_OR_BOTTOM,
               checked: sortProjectTopOrBottomCheckState
            },
            {
               type: "button",
               name: this._dialogWidgetName.projectControlButton.up,
               text: i18n.UP,
               size: "small"
            },
            {
               type: "button",
               name: this._dialogWidgetName.projectControlButton.down,
               text: i18n.DOWN,
               size: "small"
            },
            {
               type: "label",
               name: this._dialogWidgetName.projectControlButton.separator,
               text: " | ",
            },
            {
               type: "button",
               name: this._dialogWidgetName.projectControlButton.delete,
               text: i18n.DELETE,
               size: "small"
            },
            {
               type: "button",
               name: this._dialogWidgetName.projectControlButton.deleteAll,
               text: i18n.DELETE_ALL,
               size: "small"
            },
            {
               type: "label",
               name: this._dialogWidgetName.projectControlButton.separator,
               text: " | ",
            },
            {
               type: "button",
               name: this._dialogWidgetName.projectControlButton.backup,
               text: i18n.BACKUP,
               size: "small"
            }]
         },
      ];

      if (this._backupList.length) {
         dialogHeight += 400;
         formItems.push({
            type: "list",
            title: i18n.BACKUP,
            name: this._dialogWidgetName.backuplist,
            columnStretches: [1, 2],
            multiSelection: false,
            items: this.createListWidgetItems(this._backupList),
            value: selectedBackupIndex
         }, 
         {
            type: "widgetGroup",
            name: this._dialogWidgetName.backupControlButtonGroup,
            widgets: [
               {
                  type: "button",
                  name: this._dialogWidgetName.backupControlButton.rename,
                  text: i18n.RENAME,
                  size: "small"
               },
               {
                  type: "label",
                  name: this._dialogWidgetName.backupControlButton.separator,
                  text: " | ",
               },
               {
                  type: "button",
                  name: this._dialogWidgetName.backupControlButton.delete,
                  text: i18n.DELETE,
                  size: "small"
               },
               {
                  type: "button",
                  name: this._dialogWidgetName.backupControlButton.deleteAll,
                  text: i18n.DELETE_ALL,
                  size: "small"
               },
               {
                  type: "label",
                  name: this._dialogWidgetName.backupControlButton.separator,
                  text: " | ",
               },
               {
                  type: "button",
                  name: this._dialogWidgetName.backupControlButton.revocer,
                  text: i18n.RECOVER,
                  size: "small"
               }]
         })
      }
      else{
         dialogHeight = 600;
      }

      return {
         title: dialogTitle,
         subtitle: dialogSubTitle,
         width: dialogWidth,
         height: dialogHeight,
         customButtons: [{
            text: i18n.CLOSE
         },{
            text: i18n.OK,
            role: "accept"
         }],
         formItems: formItems
      }
   }

   private createListWidgetItems(rows: string[][]) {
      return rows.map((row: string[]) => {
         return {
            columns: row.map(column => {
               return {
                  label: `${column}`
               }
            })
         }
      })
   }
   
   private async onChanged(field: any, value: any, allData: any): Promise<boolean> {
      this.syncDialogWidgetData(allData);
      
      switch (field){
         case this._dialogWidgetName.projectControlButtonGroup: {
            switch (value.changedWidget.name) {
               case this._dialogWidgetName.projectControlButton.up: {
                  if (this._diglogWidgetData.selectedProjectIndex === 0) {
                     return false;
                  }
            
                  const selectedProject = this._currentGroupProjects[this._diglogWidgetData.selectedProjectIndex];
                  let projects = this._currentGroupProjects.filter((_, index) => index !== this._diglogWidgetData.selectedProjectIndex);
            
                  if (!this._diglogWidgetData.sortProjectTopOrBottomCheckState) {
                     this._currentGroupProjects = projects.slice(0, this._diglogWidgetData.selectedProjectIndex - 1).concat([selectedProject]).concat(projects.slice(this._diglogWidgetData.selectedProjectIndex - 1));
                     this._diglogWidgetData.selectedProjectIndex = Math.max(this._diglogWidgetData.selectedProjectIndex - 1, 0);
                  }
                  else {
                     this._currentGroupProjects = [selectedProject].concat(projects);
                     this._diglogWidgetData.selectedProjectIndex = 0;
                  }
            
                  return true;
               }
               case this._dialogWidgetName.projectControlButton.down: {
                  if (this._diglogWidgetData.selectedProjectIndex === this._currentGroupProjects.length - 1) {
                     return false;
                  }
            
                  const selectedProject = this._currentGroupProjects[this._diglogWidgetData.selectedProjectIndex];
                  let projects = this._currentGroupProjects.filter((_, index) => index !== this._diglogWidgetData.selectedProjectIndex);
            
                  if (!this._diglogWidgetData.sortProjectTopOrBottomCheckState) {
                     this._currentGroupProjects = projects.slice(0, this._diglogWidgetData.selectedProjectIndex + 1).concat([selectedProject]).concat(projects.slice(this._diglogWidgetData.selectedProjectIndex + 1));
                     this._diglogWidgetData.selectedProjectIndex = Math.min(this._diglogWidgetData.selectedProjectIndex + 1, this._currentGroupProjects.length - 1);
                  }
                  else {
                     this._currentGroupProjects = projects.concat([selectedProject]);
                     this._diglogWidgetData.selectedProjectIndex = this._currentGroupProjects.length - 1;
                  }
            
                  return true
               }
               case this._dialogWidgetName.projectControlButton.delete: {
                  this._currentGroupProjects = this._currentGroupProjects.filter((_, index) => index !== this._diglogWidgetData.selectedProjectIndex)
                  this._diglogWidgetData.selectedProjectIndex = Math.max(this._diglogWidgetData.selectedProjectIndex - 1, 0);
                  return true;
               }
               case this._dialogWidgetName.projectControlButton.deleteAll: {
                  this._currentGroupProjects = [];
                  this._diglogWidgetData.selectedProjectIndex = 0;
                  return true;
               }
               case this._dialogWidgetName.projectControlButton.backup:{
                  this._configer.backup(this._currentGroupName, this._currentGroupProjects);
                  this._configer.flushConfig();
            
                  this._backupList = this._configer.getBackupInfos(this._currentGroupName);
                  return true;
               }
               default:
                  break;
            }
         }
         case this._dialogWidgetName.backupControlButtonGroup:{
            switch (value.changedWidget.name){
               case this._dialogWidgetName.backupControlButton.rename:{
                  let info = this._backupList[this._diglogWidgetData.selectedBackupIndex];
                  
                  let name = await hx.window.showInputBox({
                     prompt: i18n.INPUT_ALIAS
                  });
                  
                  this._configer.renameBackup(this._currentGroupName, info[0], name);
                  this._configer.flushConfig();
                  
                  this._backupList = this._configer.getBackupInfos(this._currentGroupName);

                  return true;
               }
               case this._dialogWidgetName.backupControlButton.delete:{
                  let info = this._backupList[this._diglogWidgetData.selectedBackupIndex];
                  
                  this._configer.removeBackup(this._currentGroupName, info[0]);
                  this._configer.flushConfig();
                  
                  this._backupList = this._configer.getBackupInfos(this._currentGroupName);
                  this._diglogWidgetData.selectedBackupIndex = Math.max(this._diglogWidgetData.selectedBackupIndex - 1, 0);

                  return true;
               }
               case this._dialogWidgetName.backupControlButton.deleteAll:{
                  if(!this._backupList.length){
                     return false;
                  }
                  
                  this._configer.cleanBackup(this._currentGroupName);
                  this._configer.flushConfig();
                  
                  this._backupList = [];
                  this._diglogWidgetData.selectedBackupIndex = 0;
                  
                  return true;
               }
               case this._dialogWidgetName.backupControlButton.revocer:
                  let info = this._backupList[this._diglogWidgetData.selectedBackupIndex];
                  
                  this._currentGroupProjects = this._configer.getBackupProjects(this._currentGroupName,info[0]);
                  this._diglogWidgetData.selectedProjectIndex = 0;
                  
                  return true;
               default:
                  break;
            }
         }
         default:
            break;
      }
    
      return false;
   }
   
   private syncDialogWidgetData(allData: any) {
      this._diglogWidgetData.selectedProjectIndex = Math.max((<number>allData[this._dialogWidgetName.projectList]), 0);
      this._diglogWidgetData.sortProjectTopOrBottomCheckState = <boolean>allData[this._dialogWidgetName.projectControlButtonGroup].allWidget[0].checked;
      this._diglogWidgetData.selectedBackupIndex = Math.max((<number>allData[this._dialogWidgetName.backuplist] ?? this._diglogWidgetData.selectedBackupIndex), 0)
   }
}